package com.bwie.service.impl;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bwie.es.EsUser;
import com.bwie.poi.ExcelUtil;
import com.bwie.pojo.TbLog;
import com.bwie.pojo.TbMenu;
import com.bwie.pojo.TbProd;
import com.bwie.pojo.TbUser;
import com.bwie.service.TbUserService;
import com.bwie.mapper.TbUserMapper;
import com.bwie.state.CheckEnum;
import com.bwie.utils.KaptchaUtils;
import com.bwie.utils.MobileMessageUtil;
import com.bwie.utils.R;
import com.bwie.vo.*;
import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
* @author andy
* @description 针对表【tb_user(用户表)】的数据库操作Service实现
* @createDate 2025-04-21 09:39:39
*/
@Service
public class TbUserServiceImpl extends ServiceImpl<TbUserMapper, TbUser>
    implements TbUserService{

    @Autowired
    TbUserMapper tbUserMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    HttpServletRequest request;

    @Autowired
    HttpServletResponse response;

    @Autowired
    FastFileStorageClient fastFileStorageClient;

    @Autowired
    KafkaTemplate kafkaTemplate;

    @Autowired
    JavaMailSender javaMailSender;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public R getUserById(Integer id) {

        TbUser tbUser = tbUserMapper.selectById(id);
        if(tbUser == null) {
            return R.ERROR(404, "用户不存在");
        }

        tbUser.setUserPass(null);
        return R.OK(tbUser);
    }

    @Override
    public R adminLogin(AdminLoginVo adminLoginVo) {
        //-- 只要登录了，就要记录日志
        TbLog tbLog = new TbLog();
        tbLog.setCreateTime(new Date());
        tbLog.setLogName("登录");
        tbLog.setIp(request.getRemoteAddr());
        tbLog.setLogType(1);
        String message = "";

        //-- 获取用户信息
        TbUser tbUser = tbUserMapper.selectOne(
                new QueryWrapper<TbUser>().lambda().eq(TbUser::getUserName, adminLoginVo.getUserName())
        );
        if(tbUser == null) {
            //tbLog.setLogContent("用户不存在:" + JSON.toJSONString(adminLoginVo));
            message = "用户不存在:登录账号（" + adminLoginVo.getUserName() + "）登录密码（" + adminLoginVo.getUserPass() + "）";
            tbLog.setLogContent(message);
            kafkaTemplate.send("topic-log", JSON.toJSONString(tbLog));
            return R.ERROR(404, "用户不存在");
        }

        //-- 校验密码
        if(!tbUser.getUserPass().equals(adminLoginVo.getUserPass())) {
            message = "密码错误:登录账号（" + adminLoginVo.getUserName() + "）登录密码（" + adminLoginVo.getUserPass() + "）";
            tbLog.setLogContent(message);
            kafkaTemplate.send("topic-log", JSON.toJSONString(tbLog));
            return R.ERROR(500, "密码错误");
        }

        //-- 生成token
        String token = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "123456")
                .claim("userId", tbUser.getUserId())
                .claim("userName", tbUser.getUserName())
                .claim("String code = RandomUtil.randomNumbers(4);\n" +
                        "        KaptchaUtils.generateCaptcha(response, code);", tbUser.getNickName())
                .compact();

        //-- 缓存token(过期时间是60分钟)
        String redisKey = "token-" + tbUser.getUserId();
        stringRedisTemplate.opsForValue().set(redisKey, token, 60, TimeUnit.MINUTES);

        //-- 返回token
        tbUser.setToken(token);
        tbUser.setUserPass(null);

        message = "登录成功:登录账号（" + adminLoginVo.getUserName() + "）登录密码（" + adminLoginVo.getUserPass() + "）";
        tbLog.setLogContent(message);
        kafkaTemplate.send("topic-log", JSON.toJSONString(tbLog));

        return R.OK(tbUser);
    }

    @Override
    public R getUserList(PageInfoVo pageInfoVo) {
        //-- 构造分页参数
        Page<TbUser> page = new Page<>(pageInfoVo.getPageNum(), pageInfoVo.getPageSize());

        //-- 分页列表
        QueryWrapper<TbUser> wrapper = new QueryWrapper<>();
        if(StringUtils.isNotEmpty(pageInfoVo.getKeyWord())) {
            wrapper.lambda().like(TbUser::getUserName, pageInfoVo.getKeyWord())
                    .or()
                    .like(TbUser::getNickName, pageInfoVo.getKeyWord())
                    .or()
                    .like(TbUser::getUserMobile, pageInfoVo.getKeyWord())
                    .or()
                    .like(TbUser::getUserEmail, pageInfoVo.getKeyWord());
        }
        wrapper.lambda().orderByDesc(TbUser::getUserId);

        Page<TbUser> tbUserPage = tbUserMapper.selectPage(page, wrapper);


        return R.OK(tbUserPage);
    }

    @Override
    public R userReg(TbUser tbUser) {
        //-- 校验用户名是否重复（唯一性校验）
        TbUser one = tbUserMapper.selectOne(
                new QueryWrapper<TbUser>().lambda().eq(TbUser::getUserName, tbUser.getUserName())
        );
        if(one != null) {
            return R.ERROR(500, "用户名重复");
        }

        //-- 添加用户
        tbUser.setCreateTime(new Date());
        tbUserMapper.insert(tbUser);

        return R.OK();
    }

    @Override
    public R checkUser(TbUser tbUser) {
        //-- 判断用户是否存在
        TbUser user = tbUserMapper.selectById(tbUser.getUserId());
        if(user == null) {
            return R.ERROR(404, "用户不存在");
        }

        //-- 如果审核驳回，必须填写审核理由
        if(tbUser.getUserStatus().equals(CheckEnum.CHECK_NO.getCode())) {
            // 审核驳回
            if(StringUtils.isEmpty(tbUser.getReason())) {
                return R.ERROR(500, "审核驳回必须填写审核理由");
            }
        }

        //-- 审核
        user.setUserStatus(tbUser.getUserStatus());
        user.setReason(tbUser.getReason());
        tbUserMapper.updateById(user);

        //-- 发送异步通知（如果审核通过了，发送短信，如果审核失败了，发送邮件）

        String message = JSON.toJSONString(user);
        kafkaTemplate.send("topic-user", message);
        System.out.println("Kafka发送消息:" + message);

        return R.OK();
    }

    @Override
    public R getMyInfo() {
        //-- 获取token
        String token = request.getHeader("token");
        if(token == null) {
            return R.ERROR(500, "请先登录");
        }

        //-- 解析token
        Claims body = Jwts.parser().setSigningKey("123456").parseClaimsJws(token).getBody();
        Integer userId = body.get("userId", Integer.class);

        //-- 获取用户信息并返回
        TbUser tbUser = tbUserMapper.selectById(userId);
        return R.OK(tbUser);
    }

    @Override
    public R updateUser(TbUser tbUser) {
        TbUser user = tbUserMapper.selectById(tbUser.getUserId());
        if(user == null) {
            return R.ERROR(404, "用户不存在");
        }

        tbUserMapper.updateById(tbUser);

        return R.OK();
    }

    @Override
    public R uploadFile(MultipartFile file) {

        try {
            String extName = FileNameUtil.extName(file.getOriginalFilename());
            StorePath storePath = fastFileStorageClient.uploadFile(file.getInputStream(), file.getSize(), extName, null);
            String url = "http://192.168.80.192:8888/" + storePath.getFullPath();
            return R.OK(url);
        } catch (IOException e) {
            //throw new RuntimeException(e);
            e.printStackTrace();
            return R.ERROR(500, "上传失败");
        }
    }

    @Override
    public R deleteUser(Integer userId) {
        //-- 判断用户是否存在
        TbUser tbUser = tbUserMapper.selectById(userId);
        if(tbUser == null) {
            return R.ERROR(404, "用户不存在");
        }

        //-- 删除用户
        tbUserMapper.deleteById(userId);

        return R.OK();
    }

    @Override
    public R deleteUserAll(List<TbUser> userList) {

        for (TbUser tbUser : userList) {
            tbUserMapper.deleteById(tbUser.getUserId());
        }

        return R.OK();
    }

    @Override
    public R getMyRoleList() {
        //-- 解析token，获取用户ID
        String token = request.getHeader("token");
        if(token == null) {
            return R.ERROR(500, "请先登录");
        }

        Claims body = Jwts.parser().setSigningKey("123456").parseClaimsJws(token).getBody();
        Integer userId = body.get("userId", Integer.class);

        List<String> roleList = tbUserMapper.getUserRoleList(userId);

        return R.OK(roleList);
    }

    @Override
    public R getUserMenuTree() {
        //--1 解析token，获取用户ID
        String token = request.getHeader("token");
        if(token == null) {
            return R.ERROR(500, "请先登录");
        }

        Claims body = Jwts.parser().setSigningKey("123456").parseClaimsJws(token).getBody();
        Integer userId = body.get("userId", Integer.class);

        //--2 获取菜单列表
        List<TbMenu> listAll = tbUserMapper.getUserMenuTree(userId);

        //--3 构建菜单树
//        List<Tree<String>> treeList = TreeUtil.build(listAll, "0", (menu, tree) -> {
//            tree.setId("" + menu.getMenuId());
//            tree.setName(menu.getMenuName());
//            tree.setParentId("" + menu.getMenuPid());
//            tree.putExtra("path", menu.getMenuPath());
//        });

        //-- 构建一级菜单
        ArrayList<TbMenu> root = new ArrayList<>();
        for (TbMenu tbMenu : listAll) {
            if(tbMenu.getMenuPid() == 0) {
                root.add(tbMenu);
            }
        }

        //-- 使用递归构建子菜单
        buildTree(root, listAll);
        return R.OK(root);
    }

    @Override
    public R getRoleListByUserId(Integer userId) {
        List<String> roleList = tbUserMapper.getUserRoleList(userId);

        return R.OK(roleList);
    }

    @Override
    public R getUserListFromEs(UserPageVo userPageVo) {
        //--1 分页信息 ES 里面的分页，是0开始计数的
        Integer pageNum = 0;
        if(userPageVo.getPageNum() != null && userPageVo.getPageNum() > 0) {
            pageNum = userPageVo.getPageNum() - 1;
        }
        PageRequest pageRequest = PageRequest.of(pageNum, userPageVo.getPageSize());

        //--2 构造搜索条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 多条件查询
        if(StringUtils.isNotEmpty(userPageVo.getKeyWord())) {
            boolQueryBuilder.should(
                    QueryBuilders.matchQuery("userName", userPageVo.getKeyWord())
            );
            boolQueryBuilder.should(
                    QueryBuilders.matchQuery("nickName", userPageVo.getKeyWord())
            );
            boolQueryBuilder.should(
                    QueryBuilders.matchQuery("userMobile", userPageVo.getKeyWord())
            );
            boolQueryBuilder.should(
                    QueryBuilders.matchQuery("userEmail", userPageVo.getKeyWord())
            );
        }

        // 排序
        FieldSortBuilder sortBuilder = SortBuilders.fieldSort("userId").order(SortOrder.DESC);

        // 高亮查询
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("nickName").preTags("<font color='red'>").postTags("</font>");


        //--3 开始查询
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withPageable(pageRequest)
                .withQuery(boolQueryBuilder)
                .withSort(sortBuilder)
                .withHighlightBuilder(highlightBuilder);
        SearchHits<EsUser> searchHits = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), EsUser.class);

        //--4 读取查询结果
        Long total = searchHits.getTotalHits();
        ArrayList<EsUser> records = new ArrayList<>();

        for (SearchHit<EsUser> searchHit : searchHits.getSearchHits()) {
            EsUser esUser = searchHit.getContent();

            // 获取高亮字段
            List<String> nickNameList = searchHit.getHighlightField("nickName");
            if(nickNameList != null && nickNameList.size()>0) {
                String nickName = nickNameList.get(0);
                esUser.setNickName(nickName);
            }

            records.add(esUser);
        }

        //--5 返回结果
        HashMap<String, Object> map = new HashMap<>();
        map.put("total", total);
        map.put("records", records);

        return R.OK(map);
    }

    @Override
    public void imageCode(HttpServletResponse response) {
        // 生成验证码
        String code = RandomUtil.randomNumbers(4);

        // 把验证码，放入缓存(过期时间为1分钟)
        String key = code;
        String value = code;
        stringRedisTemplate.opsForValue().set(key, value, 1, TimeUnit.MINUTES);

        // 生成验证码图片
        KaptchaUtils.generateCaptcha(response, code);
    }

    @Override
    public R sendCode(ShopperLoginVo shopperLoginVo) {
        //-- 验证图形验证码（只有图形验证码正确了，才允许发送短信）
        String key = shopperLoginVo.getImageCode();
        Boolean hasKey = stringRedisTemplate.hasKey(key);
        if(!hasKey) {
            return R.ERROR(500, "图形验证码已经过期了");
        }

        //-- 生成验证码
        String code = RandomUtil.randomNumbers(4);

        //-- 把验证码，放入缓存(过期时间为5分钟)
        String mobile = shopperLoginVo.getMobile();
        stringRedisTemplate.opsForValue().set(mobile, code, 5, TimeUnit.MINUTES);
        System.out.println("手机验证码：" + code);

        //-- 发送验证码
        MobileMessageUtil.sendMobileMessage(mobile, code);

        return R.OK();
    }

    @Override
    public R shopperLogin(ShopperLoginVo shopperLoginVo) {
        //--1 验证手机验证码
        String key = shopperLoginVo.getMobile();
        String code = stringRedisTemplate.opsForValue().get(key);
        if(code == null) {
            return R.ERROR(500, "手机验证码已经过期了");
        }
        if(!code.equals(shopperLoginVo.getMobileCode())) {
            return R.ERROR(500, "手机验证码错误");
        }

        //--2 根据手机号，读取用户信息
        TbUser tbUser = tbUserMapper.selectOne(
                new QueryWrapper<TbUser>().lambda().eq(TbUser::getUserMobile, shopperLoginVo.getMobile())
        );
        if(tbUser == null) {
            return R.ERROR(500, "用户不存在");
        }

        //--3 生成token，并放入缓存，设置过期时间(token过期时间为60分钟)
        String token = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "123456")
                .claim("userId", tbUser.getUserId())
                .claim("userName", tbUser.getUserName())
                .claim("nickName", tbUser.getNickName())
                .compact();
        String tokenKey = "token-" + tbUser.getUserId();
        stringRedisTemplate.opsForValue().set(tokenKey, token, 60, TimeUnit.MINUTES);

        //--4 返回token和用户信息
        tbUser.setUserPass(null);
        tbUser.setToken(token);

        return R.OK(tbUser);
    }

    @Override
    public void exportAllUser() {
        //-- 读取所有用户信息
        List<TbUser> tbUsers = tbUserMapper.selectList(null);

        //-- 导出Excel文件
        ExcelUtil excelUtil = new ExcelUtil(TbUser.class);
        excelUtil.exportExcel(response, tbUsers, "用户列表");

    }

    @Override
    public R importUser(MultipartFile file) {
        //-- 创建工具类
        ExcelUtil<TbUser> excelUtil = new ExcelUtil<>(TbUser.class);

        try {
            //-- 开始导入
            List<TbUser> tbUsers = excelUtil.importExcel(file.getInputStream());
            String message = "成功导入" + tbUsers.size() + "条数据";

            for (TbUser tbUser : tbUsers) {
                tbUserMapper.insert(tbUser);
            }

            return R.OK(message);
        } catch (IOException e) {
            //throw new RuntimeException(e);
            e.printStackTrace();
            return R.ERROR(500, "导入失败");
        }
    }

    @Override
    public R getRegInfo() {
        RegInfoVo regInfoVo = tbUserMapper.getRegInfo();
        return R.OK(regInfoVo);
    }

    @Override
    public R getDayRegInfo() {
        //--1 获取数据
        List<DayRegVo> dayRegVoList = tbUserMapper.getDayRegInfo();

        //--2 构建数据
        ArrayList<String> xData = new ArrayList<>();
        ArrayList<Integer> yData = new ArrayList<>();
        ArrayList<DataVo> pieData = new ArrayList<>();

        dayRegVoList.stream().forEach(item -> {
            xData.add(item.getDayName());
            yData.add(item.getRegCount());

            DataVo dataVo = new DataVo();
            dataVo.setName(item.getDayName());
            dataVo.setValue(Double.valueOf(item.getRegCount()));

            pieData.add(dataVo);
        });

        //--3 返回数据
        HashMap<String, Object> map = new HashMap<>();
        map.put("xData", xData);
        map.put("yData", yData);
        map.put("pieData", pieData);

        return R.OK(map);
    }

    private void buildTree(List<TbMenu> root, List<TbMenu> listAll) {
        for (TbMenu rootMenu : root) {
            ArrayList<TbMenu> children = new ArrayList<>();
            for (TbMenu subMenu : listAll) {
                if(subMenu.getMenuPid().equals(rootMenu.getMenuId())) {
                    children.add(subMenu);
                }
            }
            rootMenu.setChildren(children);

            // 递归调用
            buildTree(children, listAll);
        }
    }

    @KafkaListener(topics = "topic-user")
    public void recvUserMessage(String message, Acknowledgment acknowledgment) {
        System.out.println("Kafka接收到消息:" + message);

        TbUser tbUser = JSON.parseObject(message, TbUser.class);

        if(tbUser.getUserStatus().equals(CheckEnum.CHECK_OK.getCode())) {
            // 审核通过，发送短信
            String mobile = tbUser.getUserMobile();
            String content = "恭喜您，您的账号审核通过，请登录系统";
            System.out.println("发送短信:" + mobile + ":" + content);
            MobileMessageUtil.sendMobileMessage(mobile, content);
        }
        else if(tbUser.getUserStatus().equals(CheckEnum.CHECK_NO.getCode())) {
            // 审核不通过，发送邮件
            try {
                MimeMessage mimeMessage = javaMailSender.createMimeMessage();
                MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true);

                messageHelper.setFrom("3350996729@qq.com");
                messageHelper.setTo(tbUser.getUserEmail());
                messageHelper.setSubject("用户审核被驳回");
                messageHelper.setText("您的账号审核被驳回，请重新注册");

                javaMailSender.send(mimeMessage);

                System.out.println("发送邮件成功");
            } catch (MessagingException e) {
                //throw new RuntimeException(e);
                e.printStackTrace();
            }
        }

        // 手动确认消息
        acknowledgment.acknowledge();

    }

    @KafkaListener(topics = "topic-prod")
    public void recvProdMessage(String message, Acknowledgment acknowledgment) {
        System.out.println("Kafka接收到消息:" + message);

        TbProd tbProd = JSON.parseObject(message, TbProd.class);
        if(tbProd.getProdStatus() == 2) {
            // 商品审核通过，发送短信
            TbUser tbUser = tbUserMapper.selectById(tbProd.getUserId());
            String mobile = tbUser.getUserMobile();
            String content = "恭喜您，您的商品审核通过，请登录系统";
            MobileMessageUtil.sendMobileMessage(mobile, content);
        }
        else if(tbProd.getProdStatus() == 3) {
            try {
                // 商品审核不通过，发送邮件
                TbUser tbUser = tbUserMapper.selectById(tbProd.getUserId());
                String userEmail = tbUser.getUserEmail();

                MimeMessage mimeMessage = javaMailSender.createMimeMessage();
                MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true);

                messageHelper.setFrom(userEmail);
                messageHelper.setTo(userEmail);
                messageHelper.setSubject("商品审核被驳回");
                messageHelper.setText("您的商品[" + tbProd.getProdName() + "] 审核被驳回，请重新上传");

                javaMailSender.send(mimeMessage);
            } catch (MessagingException e) {
                //throw new RuntimeException(e);
                e.printStackTrace();
            }

            // 手动确认消息
            acknowledgment.acknowledge();
        }
    }
}




